﻿using System.IO;
using System.Net;
using System.Net.NetworkInformation;
using System.Windows;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using HandyControl.Controls;
using LibGit2Sharp;
using Microsoft.Extensions.DependencyInjection;
using OpenDeploy.Client.Dialogs;
using OpenDeploy.Client.WPF;
using OpenDeploy.Domain.Convention;
using OpenDeploy.Domain.Models;
using OpenDeploy.Infrastructure;
using OpenDeploy.Infrastructure.Extensions;
using OpenDeploy.SQLite;

namespace OpenDeploy.Client.Models;

/// <summary> 项目视图模型 </summary>
public partial class ProjectViewModel : ObservableObject
{
    public SolutionViewModel SolutionVM { get; init; } = default!;

    /// <summary> 项目Id </summary>
    [ObservableProperty]
    private Guid projectId;

    /// <summary> 项目名称 </summary>
    [ObservableProperty]
    private string projectName = string.Empty;

    /// <summary> 项目所在文件夹 </summary>
    [ObservableProperty]
    private string projectDir = string.Empty;

    /// <summary> 项目发布输出文件夹 </summary>
    [ObservableProperty]
    private string releaseDir = string.Empty;

    /// <summary> 是否Web项目 </summary>
    [ObservableProperty]
    private bool isWeb;

    /// <summary> 是否启用 </summary>
    [ObservableProperty]
    private bool isEnabled;

    /// <summary> Web项目发布对应的服务器IP地址(自动发布部署的服务器地址) </summary>
    [ObservableProperty]
    private string serverIp = string.Empty;

    /// <summary> 自动发布部署的服务器端口 </summary>
    [ObservableProperty]
    private int serverPort = 20007;


    /// <summary> 是否首次发布(首次发布需要人工操作) </summary>
    [ObservableProperty]
    private bool firstRelease;

    /// <summary> 首次发布Git提交ID </summary>
    [ObservableProperty]
    private string firstPublishGitCommitId = string.Empty;

    /// <summary> 上次发布时间 </summary>
    [ObservableProperty]
    private string lastPublishTime = string.Empty;

    /// <summary> 自上次发布以来的改动 </summary>
    [ObservableProperty]
    private List<PatchEntryChanges>? changesSinceLastCommit;

    /// <summary> 待发布的文件 </summary>
    [ObservableProperty]
    private List<DeployFileInfo>? publishFiles;

    /// <summary> 一键发布解决方案弹窗 </summary>
    private Dialog? quickDeployDialog;

    /// <summary>
    /// 打开发布项目弹窗
    /// </summary>
    [RelayCommand]
    private async Task OpenPublishProjectDialog()
    {
        try
        {
            this.ThrowIfConfigIsInvalid();
        }
        catch (Exception ex)
        {
            Growl.ClearGlobal();
            Growl.WarningGlobal(ex.Message);
            return;
        }

        //获取最近一次提交
        var lastGitCommit = GitHelper.GetLastCommit(SolutionVM.GitRepositoryPath);
        if (lastGitCommit == null)
        {
            Growl.ClearGlobal();
            Growl.WarningGlobal("暂未发现Git提交记录");
            return;
        }

        //获取上次发布记录
        var lastPublish = await SolutionVM.solutionRepo.GetLastPublishAsync(SolutionVM.SolutionId);

        //没有发布过,本次将执行首次发布
        if (lastPublish == null)
        {
            FirstRelease = true;
            LastPublishTime = "暂无发布记录";
        }
        else
        {
            FirstRelease = false;
            LastPublishTime = lastPublish.PublishTime.ToString("yyyy-MM-dd HH:mm:ss");

            if (lastPublish.GitCommitId == lastGitCommit.Sha)
            {
                Growl.WarningGlobal("自上次发布以来暂无新的提交记录");
                return;
            }

            //获取自上次发布以来的改动
            var changes = GitHelper.GetChangesSinceLastPublish(SolutionVM.GitRepositoryPath, lastPublish?.GitCommitId);
            if (changes.IsEmpty())
            {
                Growl.WarningGlobal("自上次发布以来暂无改动的文件");
                return;
            }
            ChangesSinceLastCommit = changes;

            //从Git变化解析出待发布的文件
            var files = GetPublishFiles(changes.Select(a => a.Path.Replace("/", "\\")));
            if (changes.IsEmpty())
            {
                Growl.WarningGlobal("自上次发布以来暂无需要发布的文件");
                return;
            }
            PublishFiles = files;
        }

        quickDeployDialog = Dialog.Show(new PublishProjectDialog(this));
    }


    /// <summary>
    /// 打开项目目录
    /// </summary>
    [RelayCommand]
    private void OpenProjectDir()
    {
        try
        {
            var projectName = System.IO.Path.Combine(ProjectDir, $"{ProjectName}.csproj");
            Helper.ShellUtil.ExplorerFile(projectName);
        }
        catch (Exception ex)
        {
            Growl.ErrorGlobal($"打开项目目录失败,可能被杀毒软件阻止了: {ex}");
        }
    }

    /// <summary>
    /// 打开项目发布目录
    /// </summary>
    [RelayCommand]
    private void OpenProjectReleaseDir()
    {
        if (string.IsNullOrEmpty(ReleaseDir))
        {
            Growl.ClearGlobal();
            Growl.WarningGlobal("请配置项目发布目录");
            return;
        }
        try
        {
            Helper.ShellUtil.ExplorerFile(ReleaseDir);
        }
        catch (Exception ex)
        {
            Growl.ErrorGlobal($"打开项目发布目录失败,可能被杀毒软件阻止了: {ex}");
        }
    }

    /// <summary> 设置项目发布路径弹窗 </summary>
    private Dialog? setProjectReleaseDirDialog;

    /// <summary>
    /// 设置项目发布信息 - 打开弹窗
    /// </summary>
    [RelayCommand]
    private void OpenSetProjectReleaseDirDialog()
    {
        setProjectReleaseDirDialog = Dialog.Show(new SetProjectReleaseDirDialog(this));
    }

    /// <summary>
    /// 设置项目发布信息
    /// </summary>
    [RelayCommand]
    private async Task OkSetProjectReleaseDir()
    {
        try
        {
            ThrowIfConfigIsInvalid();
        }
        catch (Exception ex)
        {
            Growl.ClearGlobal();
            Growl.WarningGlobal(ex.Message);
            return;
        }

        var solutionRepo = Program.AppHost.Services.GetRequiredService<SolutionRepository>();
        await solutionRepo.UpdateProjectReleaseInfo(ProjectId, ReleaseDir, ServerIp, ServerPort);

        setProjectReleaseDirDialog?.Close();

        Growl.SuccessGlobal("操作成功");
    }

    /// <summary>
    /// 禁用项目
    /// </summary>
    [RelayCommand]
    private async Task DisableProject()
    {
        this.IsEnabled = false;
        await SolutionVM.DisableProjectAsync(this);
    }








    /// <summary> 确定发布项目 </summary>
    [RelayCommand]
    private async Task OkPublishProject()
    {
        //首次发布
        if (FirstRelease)
        {
            await RunFirstPublishAsync();
            return;
        }

        //await RunPublishAsync();
    }

    /// <summary>
    /// 首次人工发布,只记录提交Id
    /// </summary>
    private async Task RunFirstPublishAsync()
    {
        if (string.IsNullOrEmpty(FirstPublishGitCommitId))
        {
            Growl.ClearGlobal();
            Growl.WarningGlobal($"请输入Git提交ID");
            return;
        }
        if (!GitHelper.ExistsCommit(SolutionVM.GitRepositoryPath, FirstPublishGitCommitId))
        {
            Growl.ClearGlobal();
            Growl.WarningGlobal($"请输入正确的Git提交ID");
            return;
        }
        //保存首次人工发布记录
        await SolutionVM.solutionRepo.SaveProjectFirstPublishAsync(
            SolutionVM.SolutionId, 
            SolutionVM.SolutionName, 
            this.ProjectId,
            this.ProjectName,
            FirstPublishGitCommitId);
        Growl.SuccessGlobal($"操作成功");
        quickDeployDialog?.Close();
    }










    /// <summary>
    /// 检查项目配置
    /// </summary>
    public void ThrowIfConfigIsInvalid()
    {
        if (string.IsNullOrEmpty(ReleaseDir) || !Directory.Exists(ReleaseDir))
        {
            throw new Exception("请正确设置Web项目的发布路径");
        }

        if (string.IsNullOrEmpty(ServerIp) || !IPAddress.TryParse(ServerIp, out _))
        {
            throw new Exception("请正确设置Web项目的服务器的IP地址");
        }

        if (ServerPort <= 0 || ServerPort > 65535)
        {
            throw new Exception("请正确设置Web项目对应的自动发布工具在服务器的端口");
        }
    }








    /// <summary>
    /// 从Git修改记录提取出待发布文件
    /// </summary>
    private List<DeployFileInfo> GetPublishFiles(IEnumerable<string> changedFilePaths)
    {
        var fileInfos = new List<DeployFileInfo>(changedFilePaths.Count());
        foreach (string changedPath in changedFilePaths)
        {
            var fi = DeployFileInfo.Create(changedPath);
            if (fi.IsUnKnown) continue;
            fileInfos.Add(fi);
        }
        foreach (var fi in fileInfos)
        {
            fi.ChangedFileAbsolutePath = Path.Combine(SolutionVM.GitRepositoryPath, fi.ChangedFileRelativePath);

            //所属项目
            var project = SolutionVM.AllProjects
                .Where(a => fi.ChangedFileRelativePath.Contains(a.ProjectName, StringComparison.OrdinalIgnoreCase))
                .FirstOrDefault();
            if (project == null)
            {
                Logger.Info(fi.ToJsonString(true));
                continue;
            }

            fi.ProjectName = project.ProjectName;
            if (fi.IsDLL)
            {
                fi.FileName = $"{project.ProjectName}.dll";
                fi.PublishFileRelativePath = $"bin\\{fi.FileName}";
            }
            else
            {
                fi.PublishFileRelativePath = fi.ChangedFileAbsolutePath.Replace(project.ProjectDir, "").TrimStart(Path.DirectorySeparatorChar);
            }
            fi.PublishFileAbsolutePath = Path.Combine(this.ReleaseDir, fi.PublishFileRelativePath);

            Logger.Info(fi.ToJsonString(true));
        }
        //按照 PublishFileAbsolutePath 去重
        return fileInfos.Distinct(new DeployFileInfoComparer()).ToList();
    }
}
